﻿using System.Collections.Concurrent;
using System.ComponentModel;
using System.Reflection;

using ThingsGateway.NewLife.Reflection;

namespace ThingsGateway.NewLife;

/// <summary>特性辅助类</summary>
public static class AttributeX
{
    #region 静态方法
    private static readonly ConcurrentDictionary<String, Object> _asmCache = new();

    /// <summary>获取自定义属性，带有缓存功能，避免因.Net内部GetCustomAttributes没有缓存而带来的损耗</summary>
    /// <typeparam name="TAttribute"></typeparam>
    /// <param name="assembly"></param>
    /// <returns></returns>
    public static TAttribute[] GetCustomAttributes<TAttribute>(this Assembly assembly)
    {
        if (assembly == null) return Array.Empty<TAttribute>();

        var key = $"{assembly.FullName}_{typeof(TAttribute).FullName}";

        return (TAttribute[])_asmCache.GetOrAdd(key, k =>
        {
            var atts = assembly.GetCustomAttributes(typeof(TAttribute), true) as TAttribute[];
            return atts ?? (Array.Empty<TAttribute>());
        });
    }

    /// <summary>获取成员绑定的显示名</summary>
    /// <param name="member"></param>
    /// <param name="inherit"></param>
    /// <returns></returns>
    public static String? GetDisplayName(this MemberInfo member, Boolean inherit = true)
    {
        var att = member.GetCustomAttribute<DisplayNameAttribute>(inherit);
        if (att?.DisplayName.IsNullOrWhiteSpace() == false) return att.DisplayName;

        return null;
    }

    /// <summary>获取成员绑定的备注</summary>
    /// <param name="member"></param>
    /// <param name="inherit"></param>
    /// <returns></returns>
    public static String? GetDescription(this MemberInfo member, Boolean inherit = true)
    {
        var att2 = member.GetCustomAttribute<DescriptionAttribute>(inherit);
        if (att2?.Description.IsNullOrWhiteSpace() == false) return att2.Description;

        return null;
    }

    /// <summary>获取自定义属性的值。可用于ReflectionOnly加载的程序集</summary>
    /// <typeparam name="TAttribute"></typeparam>
    /// <typeparam name="TResult"></typeparam>
    /// <returns></returns>
    public static TResult? GetCustomAttributeValue<TAttribute, TResult>(this Assembly target) where TAttribute : Attribute
    {
        if (target == null) return default;

        // CustomAttributeData可能会导致只反射加载，需要屏蔽内部异常
        try
        {
            var list = CustomAttributeData.GetCustomAttributes(target);
            if (list == null || list.Count <= 0) return default;

            foreach (var item in list)
            {
                if (typeof(TAttribute) != item.Constructor.DeclaringType) continue;

                var args = item.ConstructorArguments;
                if (args?.Count > 0) return (TResult?)args[0].Value;
            }
        }
        catch { }

        return default;
    }

    /// <summary>获取自定义属性的值。可用于ReflectionOnly加载的程序集</summary>
    /// <typeparam name="TAttribute"></typeparam>
    /// <typeparam name="TResult"></typeparam>
    /// <param name="target">目标对象</param>
    /// <param name="inherit">是否递归</param>
    /// <returns></returns>
    public static TResult? GetCustomAttributeValue<TAttribute, TResult>(this MemberInfo target, Boolean inherit = true) where TAttribute : Attribute
    {
        if (target == null) return default;

        try
        {
            var list = CustomAttributeData.GetCustomAttributes(target);
            if (list?.Count > 0)
            {
                foreach (var item in list)
                {
                    if (typeof(TAttribute).FullName != item.Constructor.DeclaringType?.FullName) continue;

                    var args = item.ConstructorArguments;
                    if (args?.Count > 0) return (TResult?)args[0].Value;
                }
            }
            if (inherit && target is Type type && type.BaseType != null)
            {
                target = type.BaseType;
                if (target != null && target != typeof(Object))
                    return GetCustomAttributeValue<TAttribute, TResult>(target, inherit);
            }
        }
        catch
        {
            // 出错以后，如果不是仅反射加载，可以考虑正面来一次
            if (!target.Module.Assembly.ReflectionOnly)
            {
                //var att = GetCustomAttribute<TAttribute>(target, inherit);
                var att = target.GetCustomAttribute<TAttribute>(inherit);
                if (att != null)
                {
                    var pi = typeof(TAttribute).GetProperties().FirstOrDefault(p => p.PropertyType == typeof(TResult));
                    if (pi != null) return (TResult?)att.GetValue(pi);
                }
            }
        }

        return default;
    }

    #endregion
}